#ifndef LUAEIGEN_TENSOR_HPP
#define LUAEIGEN_TENSOR_HPP

	#include <unsupported/Eigen/CXX11/Tensor>
	#include <log/log.h>

	template<class T>
	sol::object getter(const Eigen::Tensor<T,4>& vec, sol::table key, sol::this_state L);
	template<class T,class T1>
	void setter(Eigen::Tensor<T,4>& vec, sol::table key, T1 val);

	template<class T>
	sol::object getter3(const Eigen::Tensor<T,3>& vec, sol::table key, sol::this_state L);
	template<class T,class T1>
	void setter3(Eigen::Tensor<T,3>& vec, sol::table key, T1 val);

	namespace Tensor{
		std::string serialize( const Eigen::Tensor<double,3>& vec );
		std::string serialize( const Eigen::Tensor<double,4>& vec );
		std::string serialize( const Eigen::Tensor<std::complex<double>,3>& vec );
		std::string serialize( const Eigen::Tensor<std::complex<double>,4>& vec );

		/*
		template<class T,class CONT>
		CONT chip(Eigen::Tensor<T,4>& t,const std::vector<int>& ii
								,const std::vector<int>& jj
								,const std::vector<int>& kk
								,const std::vector<int>& ll){
			Eigen::Tensor<T,4> res(ii.size(),jj.size(),kk.size(),ll.size());
			for(int i=0; i<ii.size();i++){
				for(int j=0; j<jj.size();j++){
					for(int k=0; k<kk.size();k++){
						for(int l=0; l<ll.size();l++){
							res(i,j,k,l)=t(ii[i],jj[j],kk[k],ll[l]);
						}
					}
				}
			}
			int num = ii.size()*jj.size()*kk.size()*ll.size();
			Eigen::Tensor<T, 1> res1 = res.reshape(Eigen::array<int, 1>({num}));
			auto vec = CONT(num);
			for(int i=0; i<num; i++){
				vec[i] = res1[i];
			}
			return std::move(vec);
		}
		*/
		template<class T, class CONT>
		CONT chip(Eigen::Tensor<T, 4>& t, const std::vector<int>& ii,
		          const std::vector<int>& jj, const std::vector<int>& kk, const std::vector<int>& ll) {

		    Eigen::Tensor<T, 4> res(static_cast<Eigen::DenseIndex>(ii.size()),
		                            static_cast<Eigen::DenseIndex>(jj.size()),
		                            static_cast<Eigen::DenseIndex>(kk.size()),
		                            static_cast<Eigen::DenseIndex>(ll.size()));

		    for (Eigen::DenseIndex i = 0; i < ii.size(); ++i) {
		        for (Eigen::DenseIndex j = 0; j < jj.size(); ++j) {
		            for (Eigen::DenseIndex k = 0; k < kk.size(); ++k) {
		                for (Eigen::DenseIndex l = 0; l < ll.size(); ++l) {
		                    res(i, j, k, l) = t(static_cast<Eigen::DenseIndex>(ii[i]),
		                                        static_cast<Eigen::DenseIndex>(jj[j]),
		                                        static_cast<Eigen::DenseIndex>(kk[k]),
		                                        static_cast<Eigen::DenseIndex>(ll[l]));
		                }
		            }
		        }
		    }

		    Eigen::DenseIndex num = ii.size() * jj.size() * kk.size() * ll.size();
		    Eigen::Tensor<T, 1> res1 = res.reshape(Eigen::array<Eigen::DenseIndex, 1>({num}));

		    CONT vec(num);
		    for (Eigen::DenseIndex i = 0; i < num; ++i) {
		        vec[i] = res1(i);
		    }

		    return vec;
		}

	}

#endif
